package com.wjy.game.service;

import com.alibaba.fastjson.JSON;
import com.wjy.game.config.ThreadPoolConfig;
import com.wjy.game.message.ReqMessageData;
import com.wjy.game.message.Request;
import com.wjy.game.message.ResMessageData;
import com.wjy.game.message.Response;
import com.wjy.game.message.data.res.WelcomeRes;
import com.wjy.game.utils.ProtostuffUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.List;

/**
 * @author wjy
 * @date 2025/5/22 10:05
 * @description 服务端和客户端的socket服务
 */
@Slf4j
@Setter
@Component
@Scope("prototype")
public class ServerSocketService {

    private Socket socket;

    private transient DataInputStream is;

    private transient DataOutputStream os;

    @Resource
    private List<MessageHandleService> handlers;

    @Async(ThreadPoolConfig.ASYNC_EXECUTOR_NAME)
    public void run() {
        feedback(Response.success(null, new WelcomeRes()));
        while (checkConnect()) {
            Request request = receive();
            if (request != null) {
                MDC.put("traceId", request.getTraceId());
                for (MessageHandleService handler : handlers) {
                    if (handler.support(request.getObj().operate())) {
                        Response response = handler.handle(request);
                        feedback(response);
                    }
                }
                MDC.remove("traceId");
            }
        }
    }

    public <T extends ResMessageData> void feedback(Response<T> response) {
        if (checkConnect()) {
            byte[] data = ProtostuffUtil.serialize(response);
            try {
                if (os == null) {
                    os = new DataOutputStream(socket.getOutputStream());
                }
                if (data != null) {
                    log.info("向客户端发送数据 | {}", JSON.toJSONString(response));
                    os.writeInt(data.length);
                    os.write(data);
                    os.flush();
                }
            } catch (IOException e) {
                try {
                    socket.close();
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                log.error("向客户端发送数据发生异常，异常信息：{}", e.getMessage());
            }
        }
    }

    public <T extends ReqMessageData> Request<T> receive() {
        if (checkConnect()) {
            try {
                if (is == null) {
                    is = new DataInputStream(socket.getInputStream());
                }
                int len = is.readInt();
                byte[] data = new byte[len];
                is.readFully(data);
                Request obj = ProtostuffUtil.deserialize(data, Request.class);
                MDC.put("traceId", obj.getTraceId());
                log.info("接收到客户端数据 | {}", JSON.toJSONString(obj));
                return obj;
            } catch (IOException e) {
                try {
                    socket.close();
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                log.error("从客户端收数据发生异常，异常信息：{}", e.getMessage());
                return null;
            }
        }
        return null;
    }

    private boolean checkConnect() {
        boolean isConnect = socket != null && !socket.isClosed() && socket.isConnected();
        if (!isConnect) {
            log.warn("未建立连接或连接已断开");
        }
        return isConnect;
    }
}
